package com.gigi.service.manual;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gigi.entity.GigiClassEntity;
import com.gigi.entity.SignInEntity;
import com.gigi.entity.SignInRecordEntity;
import com.gigi.enums.ErrorCodeEnum;
import com.gigi.enums.SignInRecordStateEnum;
import com.gigi.enums.SignInTypeEnum;
import com.gigi.enums.YesOrNoEnum;
import com.gigi.mapper.manual.JoinCourseManualMapper;
import com.gigi.mapper.manual.SignInManualMapper;
import com.gigi.model.*;
import com.gigi.service.generated.GigiClassService;
import com.gigi.service.generated.SignInRecordService;
import com.gigi.service.generated.SignInService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class SignInManageService {

    private final SignInService signInService;
    private final SignInManualMapper signInManualMapper;
    private final JoinCourseManualMapper joinCourseManualMapper;
    private final SignInRecordService signInRecordService;
    private final GigiClassService gigiClassService;

    public SignInManageService(SignInService signInService, SignInManualMapper signInManualMapper,
                               JoinCourseManualMapper joinCourseManualMapper, SignInRecordService signInRecordService,
                               GigiClassService gigiClassService) {
        this.signInService = signInService;
        this.signInManualMapper = signInManualMapper;
        this.joinCourseManualMapper = joinCourseManualMapper;
        this.signInRecordService = signInRecordService;
        this.gigiClassService = gigiClassService;
    }

    /**
     * 发布签到接口
     *
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Response insert(SignInDO request) {

        // 发布签到
        SignInEntity entity = new SignInEntity();
        entity.setUserId(request.getUserId());
        entity.setCourseId(request.getCourseId());
        entity.setStartTime(LocalDateTime.now());
        if (request.getType() == 2) {
            entity.setLocation(request.getLocation());
            entity.setLocationRange(request.getLocationRange());
        }
        entity.setType(request.getType());
        if (request.getType() == 0) {
            entity.setPwd(request.getPwd());
        }
        if (!signInService.save(entity)) {
            return Response.buildFailure(ErrorCodeEnum.SAVE_FAILED.getErrCode(), ErrorCodeEnum.SAVE_FAILED.getErrDesc());
        }

        // 先查课程成员
        List<JoinCourseDO> joinCourseDOS = joinCourseManualMapper.listCourseMember(request.getCourseId());
        List<Long> memberUserIds = joinCourseDOS.stream().map(JoinCourseDO::getStudentId).collect(Collectors.toList());
        if (CollUtil.isEmpty(memberUserIds)) {
            return Response.buildSuccess();
        }

        // 都示为缺勤记入表中
        if (!saveUnSignRecord(memberUserIds, entity.getId())) {
            return Response.buildFailure(ErrorCodeEnum.SAVE_FAILED.getErrCode(), ErrorCodeEnum.SAVE_FAILED.getErrDesc());
        }
        return Response.buildSuccess();
    }

    private boolean saveUnSignRecord(List<Long> unSignUserIds, Long signInId) {
        List<SignInRecordEntity> targets = unSignUserIds.stream().map(e -> {
            SignInRecordEntity target = new SignInRecordEntity();
            target.setUserId(e);
            target.setState(SignInRecordStateEnum.UN_SHOW.getCode());
            target.setSignInId(signInId);
            return target;
        }).collect(Collectors.toList());
        return signInRecordService.saveBatch(targets);
    }

    /**
     * 教师签到查询接口
     *
     * @param request
     * @return
     */
    public SingleResponse<SignInDO> getForTeacher(GetSIgnInRequest request) {
        SignInDO signInDO = signInManualMapper.getForTeacher(request.getUserId());
        if (Objects.nonNull(signInDO)) {
            signInDO.setTypeDesc(SignInTypeEnum.getEnumByCode(signInDO.getType()).getDesc());
            signInDO.setClassName(getClassName(signInDO.getClassesIdStr()));
        }
        return SingleResponse.of(signInDO);
    }

    // 获取班级名
    public String getClassName(String classesIdStr) {
        LambdaQueryWrapper<GigiClassEntity> wrapper = new LambdaQueryWrapper<GigiClassEntity>()
                .select(GigiClassEntity::getName)
                .eq(GigiClassEntity::getFlag, YesOrNoEnum.YES.getCode())
                .in(GigiClassEntity::getId, StrUtil.split(classesIdStr, StrUtil.COMMA));
        List<GigiClassEntity> classEntities = gigiClassService.list(wrapper);
        List<String> names = classEntities.stream().map(GigiClassEntity::getName).collect(Collectors.toList());
        return StrUtil.join(StrUtil.COMMA, names);
    }

    /**
     * 结束签到接口
     *
     * @param request
     * @return
     */
    public Response end(IdRequest request) {
        SignInEntity oldModel = signInService.getById(request.getId());
        if (Objects.isNull(oldModel)) {
            return Response.buildFailure(ErrorCodeEnum.NO_DATA.getErrCode(), ErrorCodeEnum.NO_DATA.getErrDesc());
        }
        SignInEntity target = new SignInEntity();
        target.setId(oldModel.getId());
        target.setState(YesOrNoEnum.YES.getCode());
        target.setEndTime(LocalDateTime.now());
        boolean isSuccess = signInService.updateById(target);
        if (!isSuccess) {
            return Response.buildFailure(ErrorCodeEnum.EDIT_FAILED.getErrCode(), ErrorCodeEnum.EDIT_FAILED.getErrDesc());
        }
        return Response.buildSuccess();
    }

    /**
     * 学生签到查询
     *
     * @param request
     * @return
     */
    public SingleResponse<SignInDO> getForStudent(GetSIgnInRequest request) {
        SignInDO signInDO = signInManualMapper.getForStudent(request.getUserId());
        if (Objects.nonNull(signInDO)) {
            signInDO.setTypeDesc(SignInTypeEnum.getEnumByCode(signInDO.getType()).getDesc());
        }
        return SingleResponse.of(signInDO);
    }

    /**
     * 签到列表接口
     *
     * @param request
     * @return
     */
    public MultiResponse<SignInDO> list(ListSignInRequest request) {
        LambdaQueryWrapper<SignInEntity> wrapper = new LambdaQueryWrapper<SignInEntity>()
                .select(SignInEntity::getStartTime, SignInEntity::getEndTime, SignInEntity::getId, SignInEntity::getType)
                .eq(SignInEntity::getUserId, request.getUserId())
                .eq(SignInEntity::getState, YesOrNoEnum.YES.getCode())
                .eq(Objects.nonNull(request.getCourseId()), SignInEntity::getCourseId, request.getCourseId())
                .orderByDesc(SignInEntity::getId);
        List<SignInEntity> signInEntities = signInService.list(wrapper);
        if (CollUtil.isEmpty(signInEntities)) {
            return MultiResponse.of(Collections.emptyList(), 0);
        }

        List<SignInDO> signInDOS = signInEntities.stream().map(e -> {
            SignInDO signInDO = new SignInDO();
            signInDO.setId(e.getId());
            signInDO.setType(e.getType());
            signInDO.setTypeDesc(SignInTypeEnum.getEnumByCode(e.getType()).getDesc());
            signInDO.setStartTimeStr(DateUtil.format(e.getStartTime(), DatePattern.NORM_DATETIME_PATTERN));
            signInDO.setEndTimeStr(DateUtil.format(e.getEndTime(), DatePattern.NORM_DATETIME_PATTERN));
            return signInDO;
        }).collect(Collectors.toList());
        return MultiResponse.of(signInDOS, signInDOS.size());
    }

    /**
     * 补签列表接口
     *
     * @param request
     * @return
     */
    public MultiResponse<SignInDO> extraList(ListSignInRequest request) {
        List<SignInDO> signInDOS = signInManualMapper.extraList(request.getUserId(), LocalDateTime.now().minusMinutes(5));
        signInDOS = signInDOS.stream().distinct().collect(Collectors.toList());
        if (CollUtil.isNotEmpty(signInDOS)) {
            signInDOS.forEach(e -> {
                e.setTypeDesc(SignInTypeEnum.getEnumByCode(e.getType()).getDesc());
                e.setStartTimeStr(DateUtil.format(e.getStartTime(), DatePattern.NORM_DATETIME_PATTERN));
                e.setEndTimeStr(DateUtil.format(e.getEndTime(), DatePattern.NORM_DATETIME_PATTERN));
            });
        }
        return MultiResponse.of(signInDOS, signInDOS.size());
    }

}
